import 'dart:convert';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_public/config/public_text.dart';

import '../../config/public_color.dart';
import '../../foundation/decoration/border_decoration.dart';
import '../../model/page_status.dart';
import '../../tool/native_channel.dart';
import '../bar/public_app_bar.dart';
import '../bar/status_bar.dart';
import '../bar/web_app_bar.dart';
import '../container/circle_container.dart';
import '../indicator/error_indicator.dart';
import '../indicator/loading.dart';
import '../indicator/loading_indicator.dart';
import '../indicator/toast.dart';
import 'public_web_view.dart';
import 'tool/web_view_string.dart';

/// FileName web_view_page
///
/// @Author lax
/// @Date 2024/4/9
///
/// @Description

class JSMethod {
  // 返回上一页
  static const String popAction = 'popAction';

  // 清除缓存
  static const String clearCacheAction = 'clearCacheAction';

  // 显示返回按钮
  static const String showBackButtonAction = 'showBackButtonAction';

  // 加载指示器
  static const String showLoadingAction = 'showLoadingAction';

  // 文字提示
  static const String toastAction = 'toastAction';

  // 显示异常状态
  static const String showErrorIndicatorAction = 'showErrorIndicatorAction';

  // 浏览器打开
  static const String openUrlAction = 'openUrlAction';
}

class WebViewPage extends StatefulWidget {
  static String routerName = '/web_view_page';

  final String? filePath;

  final String? urlString;

  final String? allowingReadAccess;

  late final String? title;

  final Widget? titleWidget;

  final Widget? actionWidget;

  /// 执行JS内容
  final String? runJSString;

  /// 替换body内容
  final String? innerHTML;

  /// 事件绑定 key为事件名称 value为回调方法
  final Map<String, JavaScriptHandlerCallback>? Function(
      WebViewPageState state)? javaScriptHandlerMapBuilder;

  /// 初始化完成回调
  final Function(InAppWebViewController controller)? onWebViewCreated;

  final Function(InAppWebViewController controller, int progress)?
      onProgressChanged;

  /// 加载结束回调
  final Function(InAppWebViewController controller)? onWebViewLoadStop;

  /// 加载失败回调
  final Function(InAppWebViewController controller)? onWebViewLoadError;

  /// 控制台打印消息回调
  final Function(InAppWebViewController controller,
      ConsoleMessage consoleMessage, WebViewPageState state)? onConsoleMessage;

  /// 显示状态栏 默认false
  late final bool showStatusBar;

  /// 显示导航栏 默认false
  late final bool showAppBar;

  /// 显示加载进度 默认true
  late final bool showProgress;

  /// 显示返回按钮 默认false
  late final bool showBackButton;

  late final bool autoTitle;

  late final bool? cacheEnabled;

  late final bool? clearCache;

  late final Color? statusBarColor;

  late final Color? appBarColor;

  late final Color? titleColor;

  /// 加载提示文字
  final String? loadingText;

  WebViewPage({
    super.key,
    this.filePath,
    this.urlString,
    this.allowingReadAccess,
    String? title,
    this.titleWidget,
    this.runJSString,
    this.innerHTML,
    this.actionWidget,
    this.javaScriptHandlerMapBuilder,
    this.onWebViewCreated,
    this.onProgressChanged,
    this.onWebViewLoadStop,
    this.onWebViewLoadError,
    this.onConsoleMessage,
    bool? showStatusBar,
    bool? showAppBar,
    bool? showProgress,
    bool? showBackButton,
    bool? autoTitle,
    bool? cacheEnabled,
    bool? clearCache,
    Color? statusBarColor,
    Color? appBarColor,
    Color? titleColor,
    this.loadingText,
  }) {
    final uri = Uri.tryParse(urlString?.replaceAll('#', '%23') ?? '');
    final para = uri?.queryParameters
            .map((key, value) => MapEntry(key.toLowerCase(), value)) ??
        {};
    this.title = title ?? para['Title'.toLowerCase()];
    final trueList = ['1', 'true'];
    final falseList = ['0', 'false'];
    this.showStatusBar =
        showStatusBar ?? trueList.contains(para['ShowStatusBar'.toLowerCase()]);
    this.showAppBar =
        showAppBar ?? trueList.contains(para['ShowAppBar'.toLowerCase()]);
    this.showProgress =
        showProgress ?? !falseList.contains(para['ShowProgress'.toLowerCase()]);
    this.showBackButton = showBackButton ??
        trueList.contains(para['ShowBackButton'.toLowerCase()]);
    this.autoTitle =
        autoTitle ?? trueList.contains(para['AutoTitle'.toLowerCase()]);
    this.cacheEnabled =
        cacheEnabled ?? trueList.contains(para['CacheEnabled'.toLowerCase()]);
    this.clearCache =
        clearCache ?? trueList.contains(para['ClearCache'.toLowerCase()]);
    this.statusBarColor =
        statusBarColor ?? PublicColor.hex(para['StatusBarColor'.toLowerCase()]);
    this.appBarColor =
        appBarColor ?? PublicColor.hex(para['AppBarColor'.toLowerCase()]);
    this.titleColor =
        titleColor ?? PublicColor.hex(para['TitleColor'.toLowerCase()]);
    final style = int.tryParse(para['StatusBarStyle'.toLowerCase()].toString());
    if (style != null) {
      // 状态栏字体样式 0黑色 1白色
      if (style == 0) {
        SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
      } else {
        SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
      }
    }
  }

  @override
  WebViewPageState createState() => WebViewPageState();
}

class WebViewPageState extends State<WebViewPage> {
  InAppWebViewController? webViewController;

  PageStatus pageStatus = PageStatus.loading;

  String? title;

  bool canGoBack = false;

  String? errorMessage;
  String? errorImageUrl;
  String? errorButtonText;

  int progress = 0;

  bool showBackButton = false;

  void showErrorIndicator({
    String? message,
    String? imageUrl,
  }) {
    setState(() {
      errorMessage = message;
      errorImageUrl = imageUrl;
      errorButtonText = null;
      pageStatus = PageStatus.error;
    });
  }

  @override
  void initState() {
    super.initState();
    title = widget.title;
    if (widget.filePath == null &&
        widget.urlString == null &&
        widget.runJSString == null &&
        widget.innerHTML == null) {
      pageStatus = PageStatus.error;
    }
    errorButtonText = PublicText.goBack;
    showBackButton = widget.showBackButton;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: PublicColor.background,
      appBar: appBar(),
      body: body(),
    );
  }

  PublicAppBar? appBar() {
    return widget.showAppBar
        ? WebAppBar(
            color: widget.appBarColor,
            title: title,
            titleStyle: TextStyle(
              fontSize: 17,
              fontWeight: FontWeight.bold,
              color: widget.titleColor,
            ),
            titleWidget: widget.titleWidget,
            actions: widget.actionWidget == null ? [] : [widget.actionWidget!],
            isShowClose: canGoBack,
            onGoBack: () {
              if (canGoBack) {
                webViewController?.goBack();
              } else {
                Navigator.pop(context);
              }
            },
          )
        : null;
  }

  Widget body() {
    return Column(
      children: [
        widget.showAppBar == false && widget.showStatusBar
            ? StatusBar(color: widget.statusBarColor)
            : const SizedBox(),
        Expanded(
          child: Stack(
            children: [
              pageStatus == PageStatus.error ? errorIndicator() : webView(),
              Visibility(
                visible: pageStatus == PageStatus.loading,
                child: LoadingIndicator(text: widget.loadingText),
              ),
              Visibility(
                visible: widget.showProgress && progress < 100,
                child: LinearProgressIndicator(
                  value: max(10, progress).toDouble() / 100,
                  minHeight: 1,
                  color: Colors.blue,
                  backgroundColor: Colors.transparent,
                ),
              ),
              Visibility(
                visible: showBackButton,
                child: InkWell(
                  onTap: () {
                    if (canGoBack) {
                      webViewController?.goBack();
                    } else {
                      Navigator.pop(context);
                    }
                  },
                  child: CircleContainer(
                    size: 32,
                    decoration: BorderDecoration(
                      borderWidth: 0.5,
                      color: const Color(0x80000000),
                      borderColor: Colors.white,
                      shape: BoxShape.circle,
                    ),
                    margin: EdgeInsets.only(
                      left: 16,
                      top: widget.showAppBar
                          ? 16
                          : (MediaQuery.paddingOf(context).top + 12),
                    ),
                    alignment: Alignment.center,
                    child: const Icon(
                      Icons.arrow_back_ios_new,
                      size: 16,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  Widget errorIndicator() {
    return ErrorIndicator(
      text: errorMessage,
      imageUrl: errorImageUrl,
      reloadText: errorButtonText,
      onReload: errorButtonText == null
          ? null
          : () {
              Navigator.pop(context);
            },
    );
  }

  Widget webView() {
    return PublicWebView(
      filePath: widget.filePath,
      urlString: widget.urlString,
      allowingReadAccess: widget.allowingReadAccess,
      clearCache: widget.clearCache,
      cacheEnabled: widget.cacheEnabled,
      javaScriptHandlerMap: () {
        Map<String, JavaScriptHandlerCallback> map =
            widget.javaScriptHandlerMapBuilder?.call(this) ?? {};
        map.addAll({
          JSMethod.popAction: (_) {
            Navigator.pop(context);
          },
          JSMethod.clearCacheAction: (_) async {
            await webViewController?.clearCache();
            return '';
          },
          JSMethod.showBackButtonAction: (args) {
            Map? map;
            if (args.first is String) {
              try {
                map = jsonDecode(args.first);
              } catch (_) {}
            } else if (args.first is Map) {
              map = args.first;
            }
            if (map != null) {
              final status = map['status'];
              if (status != null) {
                setState(() {
                  showBackButton =
                      ['1', 'true'].contains(status!.toLowerCase());
                });
              }
            }
          },
          JSMethod.showLoadingAction: (args) {
            Map? map;
            if (args.first is String) {
              try {
                map = jsonDecode(args.first);
              } catch (_) {}
            } else if (args.first is Map) {
              map = args.first;
            }
            if (map != null) {
              final status = map['status'] ?? '0';
              final duration = map['duration'];
              final message = map['message'];
              if (status == '1') {
                if (message == null) {
                  Loading.show();
                } else {
                  Loading.show(message);
                }
                final seconds = double.tryParse(duration ?? '');
                if (seconds != null) {
                  Future.delayed(
                          Duration(milliseconds: (seconds * 1000).toInt()))
                      .then((value) {
                    Loading.dismiss();
                  });
                }
              } else {
                Loading.dismiss();
              }
            }
          },
          JSMethod.toastAction: (args) async {
            Map? map;
            if (args.first is String) {
              try {
                map = jsonDecode(args.first);
              } catch (_) {}
            } else if (args.first is Map) {
              map = args.first;
            }
            if (map != null) {
              final message = map['message'];
              Toast.show(message);
            }
          },
          // 显示异常状态
          JSMethod.showErrorIndicatorAction: (args) {
            Map? map;
            if (args.first is String) {
              try {
                map = jsonDecode(args.first);
              } catch (_) {}
            } else if (args.first is Map) {
              map = args.first;
            }
            if (map != null) {
              final message = map['message'];
              final imageUrl = map['imageUrl'];
              showErrorIndicator(message: message, imageUrl: imageUrl);
            }
          },
          // 在浏览器打开链接
          JSMethod.openUrlAction: (args) {
            Map? map;
            if (args.first is String) {
              try {
                map = jsonDecode(args.first);
              } catch (_) {}
            } else if (args.first is Map) {
              map = args.first;
            }
            if (map != null) {
              final url = map['url'];
              NativeChannel.openUrl(url);
            }
          },
        });
        return map;
      }(),
      onWebViewCreated: (controller) {
        webViewController = controller;
        widget.onWebViewCreated?.call(controller);
      },
      onProgressChanged: (controller, progress) {
        setState(() {
          this.progress = progress;
        });
        widget.onProgressChanged?.call(controller, progress);
      },
      onConsoleMessage: (controller, consoleMessage) async {
        // Flutter Web交互 'flutterCallJS={"method": "popAction"}'
        if (consoleMessage.message.startsWith('flutterCallJS=')) {
          final message = consoleMessage.message.substring(14);
          try {
            final object = jsonDecode(message);
            final method = object['method'];
            final args = object['args'] ?? {};
            if (method == JSMethod.popAction) {
              Navigator.pop(context);
            } else if (method == JSMethod.clearCacheAction) {
              await controller.clearCache();
              callBack(controller: controller, method: method, result: '');
            } else if (method == JSMethod.showBackButtonAction) {
              final status = args['status'] ?? '';
              setState(() {
                showBackButton = ['1', 'true'].contains(status.toLowerCase());
              });
            } else if (method == JSMethod.toastAction) {
              final message = args['message'];
              Toast.show(message);
            } else if (method == JSMethod.showErrorIndicatorAction) {
              final message = args['message'];
              final imageUrl = args['imageUrl'];
              showErrorIndicator(message: message, imageUrl: imageUrl);
            } else if (method == JSMethod.openUrlAction) {
              final url = args['url'];
              NativeChannel.openUrl(url);
            }
          } catch (e) {
            Toast.show(e.toString());
          }
        }
        widget.onConsoleMessage?.call(controller, consoleMessage, this);
      },
      onLoadStop: (controller) async {
        final runJSString = WebViewString.getEvaluateJS(
            innerHTML: widget.innerHTML, runJSString: widget.runJSString);
        if (runJSString != null) {
          controller.evaluateJavascript(source: runJSString);
        }
        final goBack = await controller.canGoBack();
        String? str = title;
        if (widget.autoTitle) {
          str = await controller.getTitle();
        }
        setState(() {
          canGoBack = goBack;
          title = str;
          if (pageStatus == PageStatus.loading) {
            pageStatus = PageStatus.normal;
          }
        });
        widget.onWebViewLoadStop?.call(controller);
      },
      onLoadError: (controller) async {
        final goBack = await controller.canGoBack();
        String? str = title;
        if (widget.autoTitle) {
          str = await controller.getTitle();
        }
        setState(() {
          canGoBack = goBack;
          title = str;
          pageStatus = PageStatus.error;
        });
        widget.onWebViewLoadError?.call(controller);
      },
    );
  }

  static void callBack({
    required InAppWebViewController controller,
    required String method,
    String? result,
  }) {
    controller.evaluateJavascript(
        source: '''window.jsCallFlutter("$method", '$result');''');
  }
}
